using AutoMapper;
using HotelManageSystem.Dto;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading.Tasks;

namespace HotelManageSystem.Data
{
    public class AuthRepository : IAuthRepository {
        private readonly DataContext _context;
        private readonly IConfiguration _configuration;
        private readonly IMapper _mapper;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public AuthRepository(DataContext context, IConfiguration configuration, IMapper mapper, IHttpContextAccessor httpContextAccessor) {
            this._context = context;
            this._configuration = configuration;
            this._mapper = mapper;
            this._httpContextAccessor = httpContextAccessor;
        }
        private int GetUserId() {
            return int.Parse(_httpContextAccessor.HttpContext!.User.FindFirstValue(ClaimTypes.NameIdentifier)!);
        }
        public async Task<ServiceResponse<object>> Login(string workerName, string password) {
            var response = new ServiceResponse<object>();

            var worker = await _context.Workers.FirstOrDefaultAsync(c=>c.Name.ToLower() == workerName.ToLower());

            if(worker is null) { 
                response.Success = false;
                response.Message = $"没有找到该员工:{workerName}。User not found.";
            }
            else if (VerifyPasswordHash(password, worker.PasswordHash, worker.PasswordSalt)) {
                response.Data = new {
                    Level = worker.PriviledgeLevel,
                    Token = CreateToken(worker)
                };
                response.Success = true;
                response.Message = "登陆成功。";
                
            }
            else {
                response.Success = false;
                response.Message = "登录失败。";
            }
            return response;
        }
        public async Task<ServiceResponse<string>> FirstRegister(AddWorkerDto newWorker) {
            var response = new ServiceResponse<string>();
            var worker = _mapper.Map<Worker>(newWorker);// 不知道对不对

            CreatePasswordHash(newWorker.Password, out byte[] passwordHash, out byte[] passwordSalt);
            worker.PasswordHash = passwordHash;
            worker.PasswordSalt = passwordSalt; 

            _context.Workers.Add(worker);
            await _context.SaveChangesAsync();

            response.Data = worker.Name;
            response.Success = true;
            response.Message = $"用户:{worker.Name}注册成功。User register complete.";
            return response;
        }
        public async Task<ServiceResponse<string>> Register(AddWorkerDto newWorker) {
            var response = new ServiceResponse<string>();

            if ((await _context.Workers.FirstOrDefaultAsync(c => c.Id == GetUserId())).PriviledgeLevel != EnumPriviledgeLevel.Admin) {
                response.Success = false;
                response.Message = "只有管理员账户才能注册账号.";
                return response;
            }

            var worker = _mapper.Map<Worker>(newWorker);// 不知道对不对

            if (string.IsNullOrEmpty(worker.Name)) {
                response.Success = false;
                response.Message = "请输入员工名。Please input the Name.";
            }
            else if (await UserExists(worker.Name)) {
                response.Success = false;
                response.Message = $"员工{worker.Name}已存在。Username already exists.";
            }
            else {
                CreatePasswordHash(newWorker.Password, out byte[] passwordHash, out byte[] passwordSalt);
                worker.PasswordHash = passwordHash;
                worker.PasswordSalt = passwordSalt;

                _context.Workers.Add(worker);
                await _context.SaveChangesAsync();
                response.Data = worker.Name;
                response.Success = true;
                response.Message = $"用户:{worker.Name}注册成功。User register complete.";
            }
            return response;
        }

        public async Task<bool> UserExists(string workerName) {
            if(await _context.Workers.AnyAsync(c => c.Name.ToLower() == workerName.ToLower())) {
                return true;
            }
            else {
                return false;
            }
        }

        private void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt) {
            using(var hmac = new System.Security.Cryptography.HMACSHA512()) {
                passwordSalt = hmac.Key;
                passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
            }
        }

        private bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt) {
            using (var hmac = new System.Security.Cryptography.HMACSHA512(passwordSalt)) {
                var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
                return computedHash.SequenceEqual(passwordHash);
            }
        }

        private string CreateToken(Worker worker) {
            var claims = new List<Claim> {
                new Claim(ClaimTypes.NameIdentifier,worker.Id.ToString()),
                new Claim(ClaimTypes.Name,worker.Name)
            };

            var appSettingsToken = _configuration.GetSection("AppSettings:Token").Value;
            if(appSettingsToken is null) {
                throw new Exception("AppSettings::Token is null!");
            }
            SymmetricSecurityKey key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(appSettingsToken));

            SigningCredentials creds = new SigningCredentials(key,SecurityAlgorithms.HmacSha512Signature);

            var tokenDescripter = new SecurityTokenDescriptor {
                Subject = new ClaimsIdentity(claims),
                Expires = DateTime.Now.AddHours(6),
                SigningCredentials = creds
            };

            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
            SecurityToken token = tokenHandler.CreateToken(tokenDescripter);

            return tokenHandler.WriteToken(token);
        }
    }
}